package win.larryzeal.md;


import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.crypto.digests.MD4Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

/**
 * MD, SHA, MAC 等加密算法。
 * 结论：
 * JDK提供的比较底层，byte[] -> byte[]，需要自己手动转换。
 * BC(bouncycastle)提供的不错。
 * CC(commons-codec)则只是简化了JDK。
 */
public class App {
    private static String message = "win.larryzeal.md.App.main()";
    private static byte[] messageByte = message.getBytes();

    public static void main(String[] args){
        // byte b = -123;
        // System.out.println(toHexString(b));

        //你妹啊，明明有这种方法
        // Integer integer = Integer.valueOf(b);
        // System.out.println(Integer.toHexString(integer).toUpperCase());

        // jdkMd2();
        // bcMd4();
        bcMd4_2();
        // jdkMd5();
        ccMd5();

    }

    public static void jdkMd2(){
        try{
            MessageDigest md2 = MessageDigest.getInstance("md2");
            byte[] digest = md2.digest(messageByte);
            // System.out.println(digest.length); // len == 16
            // System.out.println(new String(digest)); // error

            System.out.println("JDK MD2 (my version)   : " + toHexString(digest));
            System.out.println("JDK MD2 (common codec) : " + Hex.encodeHexString(digest));
        } catch(NoSuchAlgorithmException e){
            e.printStackTrace();
        }
    }


    public static void bcMd4(){
        MD4Digest md4Digest = new MD4Digest();
        md4Digest.update(messageByte, 0, messageByte.length);
        int digestSize = md4Digest.getDigestSize();
        byte[] bs = new byte[digestSize];
        md4Digest.doFinal(bs, 0);


        System.out.println("BC MD4 (my version)   : " + toHexString(bs));
        System.out.println("BC MD4 (common codec) : " + Hex.encodeHexString(bs));
        System.out.println("BC MD4 (common codec) : " + org.bouncycastle.util.encoders.Hex.toHexString(bs));
    }

    /**
     * 使用JDK + Provider
     */
    public static void bcMd4_2(){
        Security.addProvider(new BouncyCastleProvider());
        try{
            MessageDigest md5 = MessageDigest.getInstance("md4");
            byte[] digest = md5.digest(messageByte);
            // System.out.println(digest.length); // len == 16
            // System.out.println(new String(digest)); // error

            System.out.println("JDK+BC MD4 (my version)   : " + toHexString(digest));
            System.out.println("JDK+BC MD4 (common codec) : " + Hex.encodeHexString(digest));
            System.out.println("JDK+BC MD4 (BC codec) : " + org.bouncycastle.util.encoders.Hex.toHexString(digest));
        } catch(NoSuchAlgorithmException e){
            e.printStackTrace();
        }
    }

    public static void jdkMd5(){
        try{
            MessageDigest md5 = MessageDigest.getInstance("md5");
            byte[] digest = md5.digest(messageByte);
            // System.out.println(digest.length); // len == 16
            // System.out.println(new String(digest)); // error

            System.out.println("JDK MD5 (my version)   : " + toHexString(digest));
            System.out.println("JDK MD5 (common codec) : " + Hex.encodeHexString(digest));
        } catch(NoSuchAlgorithmException e){
            e.printStackTrace();
        }
    }

    //common-codecs, 没有md4
    public static void ccMd5(){
        byte[] digest = DigestUtils.md5(message);
        System.out.println("JDK MD5 (my version)   : " + toHexString(digest));
        System.out.println("JDK MD5 (common codec) : " + Hex.encodeHexString(digest));

        String str = DigestUtils.md5Hex(message);
        System.out.println("JDK MD5 (my version)   : " + str);
        System.out.println("JDK MD5 (common codec) : " + str);
    }


    public static String toHexString(byte[] bytes){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < bytes.length; i++){
            sb.append(toHexString(bytes[i]));
        }
        return sb.toString();
    }

    public static final char[] cs = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    public static final byte limit = 16;

    public static String toHexString(byte b){
        // System.out.println("byte is: " + b);
        //f**k, b可能是负数!! 负数，是32位bit；正数则不一定！！！
        String binStr = Integer.toBinaryString(b);
        // System.out.println("byte bin str is: " + binStr);
        String byteStr = binStr;
        int high = 0;
        int low = 0;
        int length = binStr.length();
        // if(b < 0){ //小于0，才是后8位数
        if(length > 8){
            byteStr = binStr.substring(length - 8);
            // System.out.println("byteStr: " + byteStr);
            high = Integer.parseUnsignedInt(byteStr.substring(0, 4), 2);
            low = Integer.parseUnsignedInt(byteStr.substring(4, 8), 2);
        } else{
            if(length > 4){
                high = Integer.parseUnsignedInt(byteStr.substring(0, length - 4), 2);
                low = Integer.parseUnsignedInt(byteStr.substring(length - 4, length), 2);
            } else{
                high = 0;
                low = Integer.parseUnsignedInt(byteStr, 2);
            }
        }
        // System.out.println(high + " - " + low);

        return cs[high] + "" + cs[low];
    }


}
